#ifndef ZDISTANCE_FUNCTION_H_
#define ZDISTANCE_FUNCTION_H_

#include "../Common/GlobalDefines.h"
#include <flann/algorithms/dist.h>

GLOBAL_NAMESPACE_BEGIN

namespace MATH
{

    enum DistanceType
    {
        DISTANCE_DEFAULT = 0,
        DISTANCE_L2,
        DISTANCE_L2_INVERSE
    };

    class DistanceFunction
    {
    public:
        typedef bool is_kdtree_distance;
        typedef double ElementType;
        typedef flann::Accumulator<double>::Type ResultType;

        //template <typename Iterator1, typename Iterator2>
        //virtual ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType) const = 0;

        //template <typename U, typename V>
        //virtual inline double accum_dist(const U& a, const V& b, int) const = 0;

        DistanceFunction(){}
        virtual DistanceType getType() const = 0;

    };

    class DistanceFuncDefault : public DistanceFunction
    {
    public:
        template <typename Iterator1, typename Iterator2>
        ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType = -1) const { return 0; }

        template <typename U, typename V>
        inline double accum_dist(const U& a, const V& b, int) const { return 0; }

        DistanceType getType() const { return DISTANCE_DEFAULT; }
    };

    typedef flann::L2 < double > L2_Distance;
    /*class L2_Distance : public DistanceFunction
    {
    public:
        DistanceType getType() const { return DISTANCE_L2; }

        template <typename Iterator1, typename Iterator2>
        ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType = -1) const
        {
            ResultType result = ResultType(0);
            ResultType diff = ResultType(0);
            for (size_t i = 0; i < size; i++)
            {
                diff = *a++ - *b++;
                result += diff*diff;
            }
            return result;
        }

        template <typename U, typename V>
        inline double accum_dist(const U& a, const V& b, int) const
        {
            return (a - b)*(a - b);
        }

    };*/

    class L2_Distance_Inverse : public DistanceFunction
    {
    public:
        DistanceType getType() const { return DISTANCE_L2_INVERSE; }

        template <typename Iterator1, typename Iterator2>
        ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType = -1) const
        {
            ResultType result = ResultType(0);
            ResultType diff = ResultType(0);
            for (size_t i = 0; i < size; i++)
            {
                diff = 1.0/((*a++)+1) - 1.0/((*b++)+1);
                result += diff*diff;
            }
            return result;
        }

        template <typename U, typename V>
        inline double accum_dist(const U& a, const V& b, int) const
        {
            U a1 = 1.0 / (a+1);
            V b1 = 1.0 / (b+1);
            return (a1 - b1)*(a1 - b1);
        }
    };

}
GLOBAL_NAMESPACE_END

#endif//ZDISTANCE_FUNCTION_H_